读取串口传感器数据
接触了很多串口通信的传感器,基本都殊途同归,就是发送一串数据,
其中包括固定的数据头,真实数据,校验和以及固定的数据尾。
用keil写过C版本的,用arduino写过c++版本的,用树莓派写过python版本的,这里记录一下。
读取的思路就是编写一个函数,这个函数每次就读取一个字节,循环调用这个函数。
函数内部就做一些标志位的判断,判断是在等待数据头还是接受数据又或者是判断校验和。
当一组数据解析完成之后再将数据更新。
以我最近使用的空气质量传感器为例,
C版本的是之前的IMU,就不放出来了,道理和C++是一样的,用结构体实现。
商品详情是这样子写的
字节 | 名称 | 说明 |
---|---|---|
B1 | 帧头1 | 固定值3Ch |
B2 | 帧头2 | 固定值02h |
B3 | 数据 | eC02高字节 |
B4 | 数据 | eCO2低字节 |
B5 | 数据 | eCH2O高字节 |
B6 | 数据 | eCH2O低字节 |
B7 | 数据 | TVOC高字节 |
B8 | 数据 | TVOC低字节 |
B9 | 数据 | PM2.5高字节 |
B10 | 数据 | PM2.5低字节 |
B11 | 数据 | PM10低字节 |
B12 | 数据 | PM10高字节 |
B13 | 数据 | Temperature 整数部分 |
B14 | 数据 | Temperature 小数部分 |
B15 | 数据 | Humidity 整数部分 |
B16 | 数据 | Humidity 小数部分 |
B17 | 校验和 | 校验和 |
c++代码
#include "AirQualitySensor.h"
AirQualitySensor air_quality_sensor;
void AirQualitySensor::init(HardwareSerial* serial_ptr)
{
serial_ptr->begin(9600);
this->serial_ptr = serial_ptr; //传入串口指针 有需要可以很方便修改成其他串口
}
const short data_length = 14;// 数据长度共14位
const short buf_bength = 17;// buf长度17位
unsigned char data_buf[buf_bength]={0};//包括数据头、数据、校验和 共17位
void AirQualitySensor::update_data()
{
static unsigned char flag = Waiting;//状态标志位
static unsigned char ReceiverFront = 0;//上一次接受的数据
static short CurrentReceiverDataNum = 0;//目前接受了多少个数据
unsigned char Receiver = 0;//当前接受的数据
if(serial_ptr->available()>0)
{
Receiver =serial_ptr->read();
}
//等待
if(flag == Waiting)
{
//数据头
if((ReceiverFront == 0x3C) && (Receiver == 0x02))
{
flag = Started;
data_buf[0] = ReceiverFront;
data_buf[1] = Receiver;
}
else
{
ReceiverFront = Receiver;
}
}
//获取数据
else
{
//将数据放入buf 包括校验和
if(CurrentReceiverDataNum < data_length +1)
{
data_buf[CurrentReceiverDataNum+2] = Receiver;
CurrentReceiverDataNum ++;
}
//数据读取完毕 检验校验和 通过则对buf解析放入类的属性
else
{
unsigned char CheckSum = 0;
for(int i=0;i<2+data_length;i++)
{
CheckSum += data_buf[i];
}
if ((CheckSum&0xff) == data_buf[buf_bength-1])
{
air_quality_sensor.CO2 = (data_buf[2]<<8) + data_buf[3];
air_quality_sensor.CH2O = (data_buf[4]<<8) + data_buf[5];
air_quality_sensor.TVOC = (data_buf[6]<<8) + data_buf[7];
air_quality_sensor.PM2_5 = (data_buf[8]<<8) + data_buf[9];
air_quality_sensor.PM10 = (data_buf[10]<<8) + data_buf[11];
if(((data_buf[12] & 0x0040)>>6)==0)
{
air_quality_sensor.temperature = CombineIntegerDecimal(data_buf[12],data_buf[13]);
}
else
{
air_quality_sensor.temperature = -CombineIntegerDecimal(data_buf[12],data_buf[13]);
}
air_quality_sensor.humidity = CombineIntegerDecimal(data_buf[14],data_buf[15]);
}
else
{
Serial.println("CheckSum error");
}
//不论校验和通不通过都重置参数
ReceiverFront =0;
CurrentReceiverDataNum =0;
flag = Waiting;
}
}
}
//合并整数和小数
float CombineIntegerDecimal(unsigned char integer,unsigned char decimal)
{
// 将整数和小数拼接成字符串再转回数字
String combine = String(integer) +"."+String(decimal);
float result = combine.toFloat();
return result;
}
#pragma once
#include <Arduino.h>
class AirQualitySensor
{
private:
HardwareSerial* serial_ptr;
public:
int CO2;//二氧化碳
int CH2O;//甲醛
int TVOC;//总挥发性有机物
int PM2_5;//PM2.5
int PM10;//PM10
float temperature;//温度
float humidity;//湿度
void init(HardwareSerial* serial_ptr);
void update_data();
};
float CombineIntegerDecimal(unsigned char integer,unsigned char decimal);
enum GetDataFlag
{
Waiting, //等待
Started //开始
};
python代码
# coding=utf-8
from enum import Enum
class Flag(Enum):
Waiting = 0
Started = 1
def bytes2int(byte):
return int.from_bytes(byte,"little",signed=False) #python读串口最麻烦的就是编码问题 注意这里要用python3
class AirQualitySensor:
def __init__(self, serial):
self._serial = serial
self.data_length = 14 # 有效数据长度
self.buf_length = 17 # buf长度
self.data_buf = [0] * self.buf_length # 包括帧头、帧尾和校验和
self.receiver = 0 # 当前接受到的数据
self.receiver_front = 0 # 上一帧接受到的数据
self.now_data_num = 0 # 目前接受了多少个数据
self.flag = Flag.Waiting # 当前flag 用于判断帧头是否满足 进而判断是否接受数据
# 数据
self.CO2 = 0
self.CH2O = 0
self.TVOC = 0
self.PM2_5 = 0
self.PM10 = 0
self.temperature = 0
self.humidity = 0
def update_data(self):
self.receiver = bytes2int(self._serial.read(1)) # 接受数据
# 等待数据头校验通过
if self.flag == Flag.Waiting:
if self.receiver_front == bytes2int(b'\x3C') and \
self.receiver == bytes2int(b'\x02'):
self.data_buf[0] = self.receiver_front
self.data_buf[1] = self.receiver
self.flag = Flag.Started
else:
self.receiver_front = self.receiver
# 获取数据
else:
# 将数据放入buf 包括最后一位校验和
if self.now_data_num + 2 < self.buf_length:
self.data_buf[self.now_data_num + 2] = self.receiver
self.now_data_num += 1
# 数据读取完毕 校验校验和
else:
check_sum = sum(self.data_buf[:-1])
if (check_sum & 0x00ff) == self.data_buf[-1]:
# 将数据放入成员变量
self.CO2 = (self.data_buf[2] << 8) + self.data_buf[3]
self.CH2O = (self.data_buf[4] << 8) + self.data_buf[5]
self.TVOC = (self.data_buf[6] << 8) + self.data_buf[7]
self.PM2_5 = (self.data_buf[8] << 8) + self.data_buf[9]
self.PM10 = (self.data_buf[10] << 8) + self.data_buf[11]
self.humidity = float(str(self.data_buf[14]) + "." + str(self.data_buf[15]))
# 温度需要判断正负
if ((self.data_buf[12] & 0x0040) >> 6) == 0:
self.temperature = float(str(self.data_buf[12]) + "." + str(self.data_buf[13]))
else:
self.temperature = -float(str(self.data_buf[12]) + "." + str(self.data_buf[13]))
else:
print("check sum error!")
# 不论是否通过检验 都重置
self.receiver = 0
self.now_data_num = 0
self.flag = Flag.Waiting